<template>
	<div class="max-w-4xl mx-auto p-6">
		<h1 class="text-2xl font-bold mb-4">为视频添加文字水印（浏览器端）</h1>

		<div class="bg-white shadow rounded p-4 mb-6">
			<p class="text-sm text-gray-600 mb-4">在浏览器端渲染文字水印并导出新视频（会同时处理音频，音调可能会变化）。</p>

			<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
				<div>
					<label class="block text-sm font-medium text-gray-700 mb-2">选择视频文件</label>
					<input type="file" accept="video/*" @change="onFileChange" class="mb-3" />

					<div v-if="originalUrl">
						<h4 class="text-sm font-medium text-gray-700 mb-2">原始预览</h4>
						<video ref="originalVideo" :src="originalUrl" controls class="w-full bg-black rounded"></video>
						<p class="text-xs text-gray-500 mt-2">时长：{{ formatTime(duration) }} · 大小：{{ formatBytes(originalSize) }}</p>
					</div>
					<div v-else class="w-full h-48 bg-gray-50 rounded flex items-center justify-center text-gray-400">还没有选择视频</div>
				</div>

				<div>
					<h4 class="text-sm font-medium text-gray-700 mb-2">水印设置</h4>

					<div class="mb-3">
						<label class="block text-sm text-gray-600 mb-1">文字内容</label>
						<input v-model="watermarkText" class="w-full border rounded px-2 py-1" placeholder="在此输入水印文字" />
					</div>

					<div class="mb-3 grid grid-cols-2 gap-2">
						<div>
							<label class="block text-sm text-gray-600 mb-1">位置</label>
							<select v-model="position" class="w-full border rounded px-2 py-1">
								<option value="top-left">左上</option>
								<option value="top-right">右上</option>
								<option value="center">居中</option>
								<option value="bottom-left">左下</option>
								<option value="bottom-right">右下</option>
							</select>
						</div>
						<div>
							<label class="block text-sm text-gray-600 mb-1">字体大小 (px)</label>
							<input type="number" v-model.number="fontSize" min="8" max="200" class="w-full border rounded px-2 py-1" />
						</div>
					</div>

					<div class="mb-3 grid grid-cols-2 gap-2">
						<div>
							<label class="block text-sm text-gray-600 mb-1">颜色</label>
							<input type="color" v-model="color" class="w-full h-10 p-0" />
						</div>
						<div>
							<label class="block text-sm text-gray-600 mb-1">透明度 (0-1)</label>
							<input type="number" step="0.1" v-model.number="opacity" min="0" max="1" class="w-full border rounded px-2 py-1" />
						</div>
					</div>

					<div class="mb-3">
						<label class="inline-flex items-center">
							<input type="checkbox" v-model="includeAudio" class="mr-2" />
							<span class="text-sm text-gray-600">保留音频</span>
						</label>
					</div>

					<div class="flex space-x-3 mt-4">
						<button @click="startWatermark" :disabled="!readyToStart || isProcessing" class="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 disabled:opacity-50">开始导出</button>
						<button @click="stopWatermark" :disabled="!isProcessing" class="px-4 py-2 bg-red-600 text-white rounded hover:bg-red-700 disabled:opacity-50">停止</button>
						<button @click="downloadResult" :disabled="!resultBlob" class="px-4 py-2 bg-gray-700 text-white rounded hover:bg-gray-800 disabled:opacity-50">下载</button>
					</div>

					<div v-if="isProcessing" class="mt-4">
						<p class="text-sm text-gray-600">进度：{{ Math.floor(progress * 100) }}%</p>
						<div class="w-full bg-gray-200 rounded h-2 mt-2 overflow-hidden"><div :style="{width: (progress*100)+'%'}" class="h-2 bg-blue-600"></div></div>
					</div>

					<div v-if="resultBlob" class="mt-4">
						<p class="text-sm text-gray-600">完成：大小 {{ formatBytes(resultSize) }}</p>
						<video :src="resultUrl" controls class="w-full bg-black rounded mt-2"></video>
					</div>
				</div>
			</div>
		</div>
	</div>
</template>

<script setup>
import { ref, onUnmounted, nextTick, computed } from 'vue'

const originalFile = ref(null)
const originalUrl = ref(null)
const originalVideo = ref(null)
const duration = ref(0)
const originalSize = ref(0)

const watermarkText = ref('示例水印')
const position = ref('bottom-right')
const fontSize = ref(32)
const color = ref('#ffffff')
const opacity = ref(0.8)
const includeAudio = ref(true)

const isProcessing = ref(false)
const progress = ref(0)

let canvas = null
let canvasCtx = null
let drawRaf = null
let captureStream = null
let mediaRecorder = null
let recordedChunks = []
let audioCtx = null
let audioSource = null
let audioDest = null

const resultBlob = ref(null)
const resultUrl = ref(null)
const resultSize = ref(0)

function formatBytes(bytes) {
	if (!bytes) return '0 B'
	const units = ['B','KB','MB','GB']
	let i = 0
	let v = bytes
	while (v >= 1024 && i < units.length-1) { v/=1024; i++ }
	return v.toFixed(2) + ' ' + units[i]
}

function formatTime(s) {
	if (!s) return '0:00'
	const m = Math.floor(s/60)
	const sec = Math.floor(s%60).toString().padStart(2,'0')
	return `${m}:${sec}`
}

function onFileChange(e) {
	stopWatermark()
	const file = e.target.files && e.target.files[0]
	if (!file) return
	originalFile.value = file
	originalSize.value = file.size
	if (originalUrl.value) URL.revokeObjectURL(originalUrl.value)
	originalUrl.value = URL.createObjectURL(file)
	resultBlob.value = null
	resultUrl.value = null

	nextTick(() => {
		const vid = originalVideo.value
		if (!vid) return
		vid.onloadedmetadata = () => { duration.value = vid.duration || 0 }
		vid.load()
	})
}

const readyToStart = computed(() => !!originalFile.value && !!originalUrl.value && !isProcessing.value)

async function startWatermark() {
	if (!originalFile.value) { alert('请先选择视频文件'); return }
	if (!('MediaRecorder' in window) || !HTMLCanvasElement.prototype.captureStream) { alert('当前浏览器不支持必要的 API（MediaRecorder 或 canvas.captureStream）'); return }

	const vid = originalVideo.value
	if (!vid) return
	if (!vid.duration || !vid.videoWidth) await new Promise(res => { vid.onloadedmetadata = res; vid.load() })

	const w = vid.videoWidth || 1280
	const h = vid.videoHeight || Math.round(w * 9/16)

	canvas = document.createElement('canvas')
	canvas.width = w
	canvas.height = h
	canvasCtx = canvas.getContext('2d')

	const fps = 25
	captureStream = canvas.captureStream(fps)

	if (includeAudio.value) {
		try {
			audioCtx = new (window.AudioContext || window.webkitAudioContext)()
			audioSource = audioCtx.createMediaElementSource(vid)
			audioDest = audioCtx.createMediaStreamDestination()
			audioSource.connect(audioDest)
			audioDest.stream.getAudioTracks().forEach(t => captureStream.addTrack(t))
		} catch(e) { console.warn('无法加入音频：', e); /* 继续，仅无音频 */ }
	}

	let mimeType = ''
	if (MediaRecorder.isTypeSupported('video/webm;codecs=vp9')) mimeType = 'video/webm;codecs=vp9'
	else if (MediaRecorder.isTypeSupported('video/webm;codecs=vp8')) mimeType = 'video/webm;codecs=vp8'
	else mimeType = 'video/webm'

	try {
		recordedChunks = []
		mediaRecorder = new MediaRecorder(captureStream, { mimeType })
	} catch (err) { console.error('无法创建 MediaRecorder：', err); alert('无法创建 MediaRecorder（可能是不支持的编码或参数）'); return }

	mediaRecorder.ondataavailable = e => { if (e.data && e.data.size > 0) recordedChunks.push(e.data) }
	mediaRecorder.onstop = onRecorderStop

	function drawLoop() {
		try {
			// 先画视频帧
			canvasCtx.drawImage(vid, 0, 0, canvas.width, canvas.height)

			// 再绘制文字水印
			if (watermarkText.value) {
				canvasCtx.save()
				canvasCtx.globalAlpha = Math.max(0, Math.min(1, opacity.value || 1))
				canvasCtx.fillStyle = color.value || '#fff'
				canvasCtx.font = `${fontSize.value || 32}px sans-serif`
				canvasCtx.textBaseline = 'middle'

				const text = watermarkText.value.toString()
				const metrics = canvasCtx.measureText(text)
				const textW = metrics.width
				const textH = fontSize.value || 32

				let x = 10
				let y = 10 + textH/2
				switch(position.value) {
					case 'top-left': x = 10; y = 10 + textH/2; break
					case 'top-right': x = canvas.width - textW - 10; y = 10 + textH/2; break
					case 'center': x = (canvas.width - textW)/2; y = canvas.height/2; break
					case 'bottom-left': x = 10; y = canvas.height - 10 - textH/2; break
					case 'bottom-right': x = canvas.width - textW - 10; y = canvas.height - 10 - textH/2; break
				}

				// 阴影以提高可读性
				canvasCtx.shadowColor = 'rgba(0,0,0,0.5)'
				canvasCtx.shadowBlur = 4
				canvasCtx.fillText(text, x, y)
				canvasCtx.restore()
			}
		} catch(e) {}
		drawRaf = requestAnimationFrame(drawLoop)
	}

	mediaRecorder.start(250)
	isProcessing.value = true
	progress.value = 0

	vid.muted = !includeAudio.value
	vid.currentTime = 0
	await vid.play()
	drawRaf = requestAnimationFrame(drawLoop)

	vid.ontimeupdate = () => { if (vid.duration) progress.value = Math.min(1, vid.currentTime/vid.duration) }
	vid.onended = () => { try { if (mediaRecorder && mediaRecorder.state !== 'inactive') mediaRecorder.stop() } catch(e){} }
}

function onRecorderStop() {
	const blob = new Blob(recordedChunks, { type: 'video/webm' })
	resultBlob.value = blob
	resultSize.value = blob.size
	if (resultUrl.value) URL.revokeObjectURL(resultUrl.value)
	resultUrl.value = URL.createObjectURL(blob)
	cleanup()
	isProcessing.value = false
	progress.value = 1
}

function stopWatermark() {
	try { if (mediaRecorder && mediaRecorder.state !== 'inactive') mediaRecorder.stop() } catch(e) {}
	if (drawRaf) { cancelAnimationFrame(drawRaf); drawRaf = null }
	cleanup()
	if (originalVideo.value) { try { originalVideo.value.pause(); originalVideo.value.currentTime = 0 } catch(e){} }
	isProcessing.value = false
	progress.value = 0
}

function cleanup() {
	if (captureStream) { captureStream.getTracks().forEach(t => { try{ t.stop() }catch(e){} }); captureStream = null }
	if (canvas) { try { canvas.width = 0; canvas.height = 0 } catch(e){} canvas = null; canvasCtx = null }
	try { if (audioSource) { try { audioSource.disconnect() } catch(e){} audioSource = null } if (audioDest) audioDest = null; if (audioCtx) { audioCtx.close().catch(()=>{}); audioCtx = null } } catch(e) {}
	mediaRecorder = null
}

function downloadResult() {
	if (!resultBlob.value) return
	const a = document.createElement('a')
	a.href = resultUrl.value
	a.download = `watermark_${new Date().toISOString().replace(/[:.]/g,'-')}.webm`
	document.body.appendChild(a)
	a.click()
	a.remove()
}

onUnmounted(() => { try { if (originalUrl.value) URL.revokeObjectURL(originalUrl.value) } catch(e){} try { if (resultUrl.value) URL.revokeObjectURL(resultUrl.value) } catch(e){} stopWatermark() })
</script>

<style scoped>
/* 保持简洁风格，与项目其他页面一致 */
</style>